<?php

/*
モバイルユーザーエージェントの時にスキンをモバイル用に変更し、必要であれば文字コードを変換します。
さらに必要であれば、指定サイズ(デフォルトは1000文字)でページ分割します。

またスキンに記述することで、アクセス中の携帯電話の情報を表示させることも出来ます。

IF拡張に対応していますので、キャリアごとに違った表示をさせることも出来ます。

キャリア別スキンに対応しています。
『使用中のスキン名/キャリア名』という名前のスキンを用意するだけで、キャリアを判別してスキンを変更させることが出来ます。
同様に『使用中のスキン名/mobile/ブログ名』または『使用中のスキン名/キャリア名/ブログ名』という名前でスキンを用意することで、ブログごとにスキンを変更することが出来ます。
*/




/**
 * NP_Mobile2, The contents of a web page are convert for cellular phones.
 *
 * NP_Mobile2 class and Convert_HTML class
 *
 * PHP versions 4 and 5
 *
 * LICENSE:
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * (see nucleus/documentation/index.html#license for more info)
 *
 * @category   Nucleus CMS plugins
 * @package    NP_Mobile2
 * @author     shizuki <shizuki@kinezumi.net>
 * @copyright  2008 shizuki
 * @license    http://nucleuscms.org/license.txt GNU General Public License
 * @version    $Id: NP_Mobile2.php 57 2009-03-03 17:53:55Z shizuki17xx $
 * @link       http://japan.nucleuscms.org/wiki/plugins:mobile2
 * @since      File available since Release 0.0.1
 */

// {{{ define

/**
 * Define skin-deliniter and default mobile-skin name.
 */
define('NPMOBILE2_SKIN_DELIM',   '/');
define('NPMOBILE2_SKIN_DEFAULT', 'mobile');
if (!defined('NPMOBILE2_PICTOGRAPH_FLG')) {
	require_once(dirname(__FILE__) . '/mobile2/pictograph.php');
}

// }}}
// {{{ NP_Mobile2

/**
 * Switch skin and change in a character code
 *
 * The skin is changed for mobility at a mobile user agent,
 * and when it's necessary, a character code is changed.
 * When it's more necessary, I do page division with the designation size
 * (for default, 1000 character).
 *
 * It's possible to be to write in skin again and make information
 * on the cellular phone I'm accessing indicate.
 *
 * It corresponds to IF expansion, so it's possible to make them
 * do different indication every career.
 *
 * It corresponds to career another skin.
 * You can just prepare skin of the name as "skin name/career name",
 * distinguish between career and change the skin.
 * It's possible to be to prepare skin under the name as "skin name/mobile/blog name"
 * or "skin name/career name/blog name" equally and change the skin every blog.
 *
 * @category   Nucleus CMS plugins
 * @package    NP_Mobile2
 * @author     shizuki <shizuki@kinezumi.net>
 * @author     NKJG <http://niku.suku.name/>
 * @copyright  2008 shizuki
 * @license    http://nucleuscms.org/license.txt GNU General Public License
 * @version    $Id: NP_Mobile2.php 57 2009-03-03 17:53:55Z shizuki17xx $
 * @link       http://japan.nucleuscms.org/wiki/plugins:mobile2
 * @since      Class available since 0.0.1
 */

/**
 * Change log
 *
 * 0.0.1    Rewritten by making reference to NP_MobileWithSinglePage.
 *          The blog another skin rewriting function and page divide function
 *          are added to NP_MobileWithSinglePage.
 *          Corresponds to rewriting to XHTML-MP.
 */
class NP_Mobile2 extends NucleusPlugin
{

	// {{{ properties

	/**
	 * Buffering situation
	 *
	 * @var bool
	 */
	var $bufStat          = false;

	/**
	 * Installed skin list
	 *
	 * @var array
	 */
	var $skinList         = array();

	/**
	 * Encode flag for comment post
	 *
	 * @var bool
	 */
	var $doCommentEncode  = false;

	/**
	 * Mobile U/A flag
	 *
	 * @var bool
	 */
	var $isMobile         = false;

	/**
	 * Mobile browser infomation
	 *
	 * @var array
	 */
	var $mobileInfo       = array();

	/**
	 * Character set for mobile browser
	 *
	 * @var string
	 */
	var $mobileCharset    = 'UTF-8';

	/**
	 * MIME Type for mobile browser
	 *
	 * @var string
	 */
	var $mimeType         = 'application/xhtml+xml';

	/**
	 * (X)HTML tags not remove for mobile
	 *
	 * @var array
	 */
	var $nonRemoveTags    = array();

	/**
	 * (X)HTML tag attributes not remove for mobile
	 *
	 * @var array
	 */
	var $nonRemoveAttrs   = array();

	/**
	 * (X)HTML tag attribute's quote remove flag
	 *
	 * @var bool
	 */
	var $quoteMode        = false;

	/**
	 * Image tag convert type
	 *
	 * @var string
	 */
	var $imageMode        = 'link';

	/**
	 * Mobile career name
	 *
	 * @var string
	 */
	var $careerName       = 'other';

	/**
	 * Pictograph name to Web-code table
	 *
	 * @var array
	 */
	var $pictWebCode      = array();

	// }}}
	// {{{ getName

	/**
	 * Returns this plugin's name
	 *
	 * @return string
	 */
	function getName()
	{
		return NPMOBILE2_PLUGIN_NAME;
	}

	// }}}
	// {{{ getAuthor

	/**
	 * Returns this plugin's author
	 *
	 * @return string
	 */
	function getAuthor()
	{
		return 'NKJG, shizuki';
	}

	// }}}
	// {{{ getVersion

	/**
	 * Returns this plugin's version
	 *
	 * @return string
	 */
	function getVersion()
	{
		return '0.0.1';
	}

	// }}}
	// {{{ getURL

	/**
	 * Returns URI of the site which can get this plugin.
	 *
	 * @return string
	 */
	function getURL()
	{
		return 'http://japan.nucleuscms.org/wiki/plugins:mobilewithsinglepage';
	}

	// }}}
	// {{{ getDescription

	/**
	 * Returns easy explanation of this plugin.
	 *
	 * @return string
	 */
	function getDescription()
	{
		return NPMOBILE2_PLUGIN_DESC;
	}

	// }}}
	// {{{ getEventList

	/**
	 * Returns list of the events by which this plugin does a hook.
	 *
	 * @return array
	 */
	function getEventList()
	{
		return array(
			'PreSendContentType',
			'InitSkinParse',
			'PreSkinParse',
			'PostSkinParse',
			'PostAuthentication',
			'FormExtra',
			'ValidateForm',
			'PreComment',
			'PreAddComment',
		);
	}

	// }}}
	// {{{ init (constructor)

	/**
	 * Initialization of a plugin.
	 */
	function init()
	{
		$this->aMobileInfo = array();
		$this->includeLangageFile();
	}

	// }}}
	// {{{ includeLangageFile

	/**
	 * Include language file for this plugin.
	 */
	function includeLangageFile()
	{
		$langName = ereg_replace('[\\|/]', '', getLanguageName());
		$langFile = $this->getDirectory() . $language . '.php';
		$engFile  = $this->getDirectory() . 'english.php';
		if (file_exists($langFile)) {
			include_once($langFile);
		} elseif (file_exists($engFile)) {
			include_once($engFile);
		}
	}

	// }}}
	// {{{ install

	/**
	 * Install actions.
	 */
	function install()
	{
		// Plugin Global Options

		// Is the skin changed at the time of access from mobility ?
		$this->createOption(
			'change_skin',
			NPMOBILE2_OPT_CHANGESKIN_DESC,
			'yesno',
			'yes'
		);

		// The name of the skin for mobility (Please set only mobile skin fixed in case of.)
		$this->createOption(
			'skin_name',
			NPMOBILE2_OPT_SKINNAME_DESC,
			'text',
			''
		);

		// The conversion way of a HTML.
		$this->createOption(
			'convert_mode',
			NPMOBILE2_OPTION_CONVMODE_DESC,
			'select',
			'XHTML',
				NPMOBILE2_OPTION_CONVMODE_OP_XHTML . '|XHTML'
			  . NPMOBILE2_OPTION_CONVMODE_OP_CHTML . '|CHTML'
			  . NPMOBILE2_OPTION_CONVMODE_OP_NOT   . '|no'
		);

		// The conversion way of img element.
		$this->createOption(
			'img_mode',
			NPMOBILE2_OPTION_IMGMODE_DESC,
			'select',
			'XHTML',
				NPMOBILE2_OPTION_IMGMODE_OP_TEXT . '|text'
			  . NPMOBILE2_OPTION_IMGMODE_OP_LINK . '|link'
			  . NPMOBILE2_OPTION_IMGMODE_OP_IMG  . '|img'
		);

		// Is a quotation mark taken (Only a HTML mode is effective.)?
		$this->createOption(
			'quote_mode',
			NPMOBILE2_OPTION_QUOTEMODE_DESC,
			'yesno',
			'no'
		);

		// Designate the elements which isn't eliminated at a change.
		// (By punctuating by "|", more than one, it's possible to establish it.)
		$this->createOption(
			'nonRemoveTags',
			NPMOBILE2_OPTION_NORMVTAGS_DESC,
			'textarea',
			'html|head|title|body|h1|h2|h3|h4|h5|h6|form|input|label|textarea|'
		  . 'ul|ol|li|br|hr|p|div|table|tr|td|th|img|a|dl|dt|dd'
		);

		// Designate the attributes which isn't eliminated at a change.
		// (By punctuating by "|", more than one, it's possible to establish it.)
		$this->createOption(
			'nonRemoveAttrs',
			NPMOBILE2_OPTION_NORMVATTRS_DESC,
			'textarea',
			'id|src|name|href|alt|action|method|enctype|accept|accpept-charset|'
		  . 'value|type|for|title|http-equiv|content|scheme|accesskey'
		);
	}

	// }}}
	// {{{ event_PreSendContentType($data)

	/**
	 * Change 'Content-Type' befor sent.
	 *
	 * @param array
	 *     'contentType' string
	 *         (pass-by-reference) reference to HTTP Content-Type
	 *     'charset'     string
	 *         (pass-by-reference) reference to HTTP Charset
	 *     'pageType'    string
	 *         (pass-by-value)     Nucleus page type
	 */
	function event_PreSendContentType($data)
	{
		if ($this->isMobile && $this->getOption('change_skin') != 'no') {
			switch ($this->getOption('convert_mode')) {
				case 'html':
					$this->mimeType      = 'text/html';
					$this->mobileCharset = 'Shift_JIS';
					break;
				case 'xhtml':
					$this->mimeType      = 'application/xhtml+xml';
					$this->mobileCharset = 'UTF-8';
					break;
				default:
					$this->mimeType      = $data['contentType'];
					$this->mobileCharset = $data['charset'];
					break;
			}
			$data['contentType'] = $this->mimeType;
			$data['charset']     = $this->mobileCharset;
		}
	}

	// }}}
	// {{{ event_InitSkinParse($data)

	/**
	 * When it's access from mobility, the skin is changed.
	 *
	 * @param array
	 *     'skin' object
	 *         (pass-by-reference) reference to definition skin
	 *     'type' string
	 *         (pass-by-value)     Nucleus page type
	 */
	function event_InitSkinParse($data)
	{
		if ($this->isMobile && $this->getOption('change_skin') != 'no') {
			$this->changeSkin(&$data['skin']);
		}
	}

	// }}}
	// {{{ event_InitSkinParse($data)

	/**
	 * When it's access from mobility, cash of a browser isn't permitted.
	 *
	 * @param array
	 *     'skin'    object
	 *         (pass-by-reference) reference to definition skin
	 *     'type'    string
	 *         (pass-by-value)     Nucleus page type
	 *     'contents' string(?)
	 *         (pass-by-reference) reference to skin contents
	 */
	function event_PreSkinParse($data)
	{
		if ($this->isMobile) {
			header('Pragma: no-cache');
			header('Cache-Control: no-cache, must-revalidate');
			if ($this->getOption('convert_mode') != 'no') {
				ob_implicit_flush(false);
				$this->bufStat = ob_start(array(&$this, 'doConvert'));
			}
		}
	}

	// }}}
	// {{{ event_PostSkinParse($data)

	/**
	 * When the skin is already changed, the contents in a buffer are output.
	 *
	 * @param array
	 *     'skin' object
	 *         (pass-by-reference) reference to definition skin
	 *     'type' string
	 *         (pass-by-value)     Nucleus page type
	 */
	function event_PostSkinParse($data)
	{
		if ($this->bufStat) {
			ob_end_flush();
		}
	}

	// }}}
	// {{{ event_PostAuthentication($data)

	/**
	 * Check $_SERVER['HTTP_USER_AGENT'] and substituted for a variable.
	 *
	 * @param array
	 *     'loggedIn' bool
	 *         (pass-by-value) User login status.
	 */
	function event_PostAuthentication($data)
	{
		global $CONF;
		if (!$CONF['UsingAdminArea']) {
			$userAgent = serverVar('HTTP_USER_AGENT');
			require_once('Net/UserAgent/Mobile.php');
			$objMobile =& Net_UserAgent_Mobile::factory($userAgent);
			if (!$objMobile->isNonMobile() && !$objMobile->isError()) {
				$this->isMobile                       = true;
				$this->mobileInfo['carrierLongName']  = $objMobile->getCarrierLongName();
				$this->mobileInfo['carrierShortName'] = $objMobile->getCarrierShortName();
				$this->careerType                     = $this->mobileInfo['carrierLongName'];
				$this->mobileInfo['name']             = $objMobile->getName();
				$this->mobileInfo['version']          = $objMobile->getVersion();
				$objDisp                              = $objMobile->getDisplay();
				if (!PEAR::isError($objDisp)) {
					$this->mobileInfo['height'] = $objDisp->getHeight();
					$this->mobileInfo['width']  = $objDisp->getWidth();
					$this->mobileInfo['color']  = $objDisp->isColor();
					$this->mobileInfo['depth']  = $objDisp->getDepth();
				}
			}
		}
	}

	// }}}
	// {{{ event_FormExtra($data)

	/**
	 * A flag is added to the access in case of from mobility and the comment form.
	 *
	 * @param array
	 *     'type' string
	 *         (pass-by-value) Form type.
	 */
	function event_FormExtra($data)
	{
		switch ($data['type']) {
			case 'commentform-loggedin' :
			case 'commentform-notloggedin' :
				break;
			default :
				return;
		}
		if ($this->isMobile && $this->getOption('convert_mode') != 'no') {
			echo '<input type="hidden" name="npmobile2" value="from_mobile" />';
		}
	}

	// }}}
	// {{{ event_ValidateForm($data)

	/**
	 * A flag is added to the access in case of from mobility and the comment form.
	 *
	 * @param array
	 *     'type' string
	 *         (pass-by-value) Form type.
	 *     'comment' array
	 *         (pass-by-refarence) Posted comment{@see COMMENTS.php isValidComment()}
	 *     'member' object
	 *         (pass-by-refarence) Added new member{@see ADMIN.php action_activatesetpwd()}
	 *     'error' bool
	 *         (pass-by-refarence) Error flag
	 *     'spamcheck' array
	 *         (pass-by-refarence) Spam check values{@see COMMENTS.php addComment()}
	 */
	function event_ValidateForm($data)
	{
		if ($data['comment']) {
			if (postVar('npmobile2') == 'from_mobile') {
				$this->doCommentEncode = true;
			}
		}
	}

	// }}}
	// {{{ event_PreAddComment($data)

	/**
	 * When a contributed comment was from mobility, before stocking in a data base,
	 * a pictograph and a character code are changed.
	 *
	 * @param array
	 *     'comment' array
	 *         (pass-by-refarence) Posted comment{@see COMMENTS.php isValidComment()}
	 *     'spamcheck' array
	 *         (pass-by-refarence) Spam check values{@see COMMENTS.php addComment()}
	 */
	function event_PreAddComment($data)
	{
		if ($this->doCommentEncode) {
			$data['comment']['user'] = mb_convert_encoding($data['comment']['user'], _CHARSET, 'auto');
			$data['comment']['body'] = mb_convert_encoding($data['comment']['body'], _CHARSET, 'auto');
		}
	}

	// }}}
	// {{{ event_PreComment($data)

	/**
	 * When a pictograph is included in a comment, I change.
	 *
	 * @param array
	 */
	function event_PreComment($data)
	{
	}

	// }}}
	// {{{ doItemVar($data)

	/**
	 * The pictograph cord described in the item is changed.
	 *
	 * @param array
	 */
	function doItemVar($data)
	{
		$emoji = NPMOBILE2_PICTOGRAPH[$data][$this->mobileCharset];
		switch (strtolower($this->careerName)) {
			case 'docomo':
			case 'willcom':
				echo '&#x' . $emoji['docomo'] . ';';
				break;
			case 'softbank':
				echo $emoji['softbank'];
				break;
			case 'ezweb':
				echo '<img localsrc="' . $emoji['ezweb'] . '" />';
				break;
			default:
				echo '<img src="' . $this->getDirectory() . '/pictograph/' . $emoji['ezweb'] . '" />';
		}
	}

	// }}}
	// {{{ doSkinVar($skinType, $name)

	/**
	 * Output MobileBrowser information.
	 *
	 * @param string
	 *     (pass-by-value) Nucleus skin type
	 * @param string
	 *     (pass-by-value) Output request
	 */
	function doSkinVar($skinType, $name = '')
	{
		$result = '';
		if (!empty($name)) {
			if (isset($this->mobileInfo[$name])) {
				if ($this->mobileInfo[$name] === true) {
					$result = 'yes';
				} elseif ($this->mobileInfo[$name] === false) {
					$result = 'no';
				} else {
					$result = $this->mobileInfo[$name];
				}
			}
		} else {
			if (!empty($this->mobileInfo)) {
				$result = "<table>\n<tbody>\n";
				foreach ($this->mobileInfo as $key => $value) {
					$result .= "<tr><td>$key</td><td>$value</td></tr>\n";
				}
				$result .= "</tbody>\n</table>\n";
			}
		}
		echo $result;
	}

	// }}}
	// {{{ doIf($key, $value)

	/**
	 * $kye is compared with $value(expansion of a skin variable "if").
	 *
	 * @param string
	 *     (pass-by-value) Compare key
	 * @param string
	 *     (pass-by-value) Compare value
	 */
	function doIf($key = '', $value = '')
	{
		$result = false;
		if ($key != '') {
			if (isset($this->mobileInfo[$key])) {
				if ($value != '') {
					if (is_numeric($value)) {
						$result = (intval($this->mobileInfo[$key]) <= intval($value));
					} else {
						$result = ($this->mobileInfo[$key] == $value);
					}
				} else {
					$result = !!($this->mobileInfo[$key]);
				}
			}
		} else {
			$result = $this->isMobile;
		}
		return $result;
	}
	
	// }}}
	// {{{ changeSkin($data)

	/**
	 * Skin is changed for something for mobility.
	 *
	 * @param object
	 *     'id'            int
	 *         (pass-by-refarence) skin ID
	 *     'description'   string
	 *         (pass-by-refarence) Description of skin
	 *     'contentType'   string
	 *         (pass-by-refarence) Content-Type for HTTP header
	 *     'includeMode'   string
	 *         (pass-by-refarence) Either 'normal' or 'skindir'
	 *     'includePrefix' string
	 *         (pass-by-refarence) skin ID
	 *     'name'          string
	 *         (pass-by-refarence) Name of skin
	 */
	function changeSkin($skin)
	{
		$currSkinName = $skin->getName();
		$newSkinName  = $this->_getNewSkinName($currSkinName);
		if ($newSkinName != $currSkinName) {
			$newSkin =& SKIN::createFromName($newSkinName);
			$skin->SKIN($newSkin->getID()); 
		}
	}

	// }}}
	// {{{ _getNewSkinName($baseName)

	/**
	 * Find the name of the skin after change.
	 *
	 * @param  string
	 *     (pass-by-value) Skin name at Nucleus (global|blog) settings
	 * @return string
	 *     Changeng skin name
	 */
	function _getNewSkinName($baseName)
	{
		global $blog;
		$result  = $baseName;
		$carrier = trim(strtolower($this->careerName));
		$optName = $this->getOption('skinName');
		$delim   = NPMOBILE2_SKIN_DELIM;
		$defSkin = NPMOBILE2_SKIN_DEFAULT;
		$skins   = array();
		$bname   = '';
		if (is_object($blog)) {
			$bname   = $blog->getName();
			$skins[] = $baseName . $delim . $carrierName . $delim . $bname;
			$skins[] = $baseName . $delim . $defSkin . $delim . $bname;
		}
		if ($optName) {
			$skins[] = $optName . $delim . $carrierName;
			$skins[] = $optName;
			if ($bname) {
				$skins[] = $optName . $delim . $carrierName . $delim . $bname;
				$skins[] = $optName . $delim . $bname;
			}
		}
		$skins[]  = $baseName . $delim . $carrierName;
		$skins[]  = $baseName . $delim . $defSkin;
		$skinList = $this->_getSkinList();
		foreach ($skins as $skin) {
			if (in_array($skin, $skinList)) {
				$result = $skin;
				break;
			}
		}
		return $result;
	}

	// }}}
	// {{{ _getSkinList

	/**
	 * Find the name of the installed skin.
	 *
	 * @return array
	 *     Installed skin name
	 */
	function _getSkinList()
	{
		if (empty($this->aSkinName)) {
			$skins  = array();
			$result = sql_query('SELECT sdname FROM ' . sql_table('skin_desc'));
			while ($res = mysql_fetch_assoc($result)) {
				$skins[] = $res['sdname'];
			}
			$this->skinList = $skins;
		}
		return $this->skinList;
	}

	// }}}
	// {{{ doConvert($strHTML)

	/**
	 * Start convertion HTML contents.
	 *
	 * @param string
	 *     (pass-by-value) HTML contents
	 * @return string
	 *     HTML contents
	 */
	function doConvert($strHTML)
	{
		$this->doConvertHTML(&$strHTML);
		$this->doConvertEncoding(&$strHTML);
		return $strHTML;
	}

	// }}}
	// {{{ doConvertHTML($strHTML)

	/**
	 * Convert HTML to C-HTML or XHTML-MP via PEAR_XML_HTMLSax3.
	 *
	 * @param string
	 *     (pass-by-refarence) HTML contents
	 * @return string
	 *     HTML contents
	 */
	function doConvertHTML($strHTML)
	{
		if (_CHARSET != 'UTF-8') {
			$strHTML = mb_convert_encoding($strHTML, 'UTF-8', _CHARSET);
		}
		$params  = array(
			'$nonRemoveTags'  => explode('|', strtolower($this->getOption('nonRemoveTags'))),
			'$nonRemoveAttrs' => explode('|', strtolower($this->getOption('nonRemoveAttrs'))),
			'quoteMode'       => $this->getOption('quote_mode') != 'no',
			'imageMode'       => $this->getOption('img_mode'),
			'mimeType'        => $this->mimeType,
			'careerName'      => $this->careerName,
		);
		$handler =& new MobileHTMLConv($params);
		require_once('XML/XML_HTMLSax3.php');
		$parser =& new XML_HTMLSax();
		$parser->set_option(XML_OPTION_CASE_FOLDING);
		$parser->set_object($handler);
		$parser->set_element_handler('openHandler', 'closeHandler');
		$parser->set_data_handler('dataHandler');
		$parser->set_escape_handler('escapeHandler');
		$parser->parse($strHTML);
		$strHTML = $handler->getResult();
		return $strHTML;
	}

	// }}}
	// {{{ doConvertEncoding($strHTML)

	/**
	 * Change charcter-set to shift_jis.
	 *
	 * @param string
	 *     (pass-by-refarence) HTML contents
	 * @return string
	 *     HTML contents
	 */
	function doConvertEncoding($strHTML)
	{
		if ($this->mimeType == 'text/html') {
			$strHTML = mb_convert_encoding($strHTML, 'SJIS', 'UTF-8');
			$strHTML = $this->doConvertHankaku($strHTML);
		}
		return $strHTML;
	}

	// }}}
	// {{{ doConvertHankaku($strHTML)

	/**
	 * Convert katakana to hankaku-katakana.
	 *
	 * @param string
	 *     (pass-by-value) HTML contents
	 * @return string
	 *     HTML contents
	 */
	function doConvertHankaku($strHTML)
	{
		$strHTML = mb_convert_kana($strHTML, 'rnask', 'SJIS');
		return $strHTML;
	}
	
	// }}}
	// {{{ getEmoji($career, $picName)

	/**
	 * The pictograph cord described in the item is changed.
	 *
	 * @param string
	 *     (pass-by-value) mobile career name
	 * @param string
	 *     (pass-by-value) pictograph name
	 */
	function getEmoji($career, $picName)
	{
	$file2no = $this->getEmoji($this->careerName, $data);
		switch (strtolower($this->careerName)) {
			case 'docomo':
			case 'willcom':
				break;
			case 'softbank':
				break;
			case 'ezweb':
				break;
			default:
		}
	}

}

// }}}
// {{{ MobileHTMLConv

/**
 * Convert HTML to XHTML-MP or C-HTML via PEAR_XML_HTMLSax3.
 *
 * A HTML is changed to XHTML or C-HTML using PEAR_XML_HTMLSax3.
 * Unnecessary elements and attributes are eliminated to shave a file size in the case.
 * It's possible to establish the elements and attributes which aren't eliminated
 * from an option of a plugin.
 * <img>tag is changed to a link to picture and indicates substitution text.
 * All <script>tags are eliminated in spite of the contents.
 * When <a>tag is doing a nest, only the outside link becomes most effective.
 *
 * @category   Nucleus CMS plugins
 * @package    NP_Mobile2
 * @author     shizuki <shizuki@kinezumi.net>
 * @author     NKJG <http://niku.suku.name/>
 * @copyright  2008 shizuki
 * @license    http://nucleuscms.org/license.txt GNU General Public License
 * @version    $Id: NP_Mobile2.php 57 2009-03-03 17:53:55Z shizuki17xx $
 * @link       http://japan.nucleuscms.org/wiki/plugins:mobile2
 * @since      Class available since 0.0.1
 */
class MobileHTMLConv
{

	// {{{ properties

	/**
	 * Elements which isn't eliminated
	 *
	 * @var array
	 */
	var $nonRemoveTags  = array();

	/**
	 * Attributes which isn't eliminated
	 *
	 * @var array
	 */
	var $nonRemoveAttrs = array();

	/**
	 * (X)HTML tag attribute's quote remove flag
	 *
	 * @var bool
	 */
	var $quoteMode      = false;

	/**
	 * Image tag convert type
	 *
	 * @var string
	 */
	var $imageMode      = 'link';

	/**
	 * Mobile career name
	 *
	 * @var string
	 */
	var $careerName     = 'other';

	/**
	 * MIME Type for mobile browser
	 *
	 * @var string
	 */
	var $mimeType       = 'application/xhtml+xml';

	/**
	 * Elements by which it's admitted that it's an empty
	 *
	 * @var array
	 */
	var $emptyElements  = array();

	/**
	 * Elements even the contents eliminated perfectly.
	 *
	 * @var array
	 */
	var $voidElements   = array();

	/**
	 * A just before element is start-tag.
	 *
	 * @var bool
	 */
	var $isEmpty        = false;

	/**
	 * Nest level of the <a> element
	 *
	 * @var int
	 */
	var $aNestLevel     = 0;

	/**
	 * Nest level of the ignore element
	 *
	 * @var int
	 */
	var $voidLevel     = 0;

	/**
	 * As a result of the processing.
	 *
	 * @var array
	 */
	var $result         = '';

	// }}}
	// {{{ MobileHTMLConv($params)

	/**
	 * Initialization of a class.
	 *
	 * @param array
	 */
	function MobileHTMLConv($params)
	{
		$this->result         = '';
		$this->nonRemoveTags  = $params['nonRemoveTags'];
		$this->nonRemoveAttrs = $params['nonRemoveAttrs'];
		$this->quoteMode      = $params['quoteMode'];
		$this->imageMode      = $params['imageMode'];
		$this->mimeType       = $params['mimeType'];
		$this->careerName     = $params['careerName'];

		$this->emptyElements = array(
			'area',
			'base',
			'basefont',
			'bgsound',
			'br',
			'embed',
			'hr',
			'img',
			'input',
			'link',
			'meta',
			'param',
			'col',
		);

		$this->voidElements = array(
			'script',
		);
		if ($this->mimeType == 'application/xhtml+xml') {
			$this->result = '<' . '?xml version="1.0" encoding="UTF-8"?' . ">\n";
		}
	}

	// }}}
	// {{{ escapeHandler(&$parser, $data)

	/**
	 * Add HTML doctype.
	 *
	 * @param
	 *
	 * @param 
	 *
	 */
	function escapeHandler(&$parser, $data)
	{
		if (preg_match("/doctype/i", $data) && $this->mimeType == 'application/xhtml+xml') {
			$this->result .= '<!DOCTYPE html PUBLIC "-//';
			switch (strtolower($this->careerName)) {
				case 'docomo':
					$this->result .= 'i-mode group (ja)//DTD XHTML i-XHTML(Locale/Ver.=ja/2.0) 1.0//EN"'
								   . ' "i-xhtml_4ja_10';
					break;
				case 'ezweb':
					$this->result .= 'OPENWAVE//DTD XHTML Mobile 1.0//EN"'
								   . ' "http://www.openwave.com/DTD/xhtml-mobile10';
					break;
				case 'softbank':
					$this->result .= 'J-PHONE//DTD XHTML Basic 1.0 Plus//EN"'
								   . ' "xhtml-basic10-plus';
					break;
				default:
					$this->result .= 'WAPFORUM//DTD XHTML Mobile 1.0//EN"'
								   . ' "http://www.wapforum.org/DTD/xhtml-mobile10';
					break;
			}
			$this->result .= '.dtd">';
		}
	}

	// }}}
	// {{{ openHandler(&$parser, $element, $attr)

	/**
	 * Process open tag.
	 *
	 * @param
	 *
	 * @param 
	 *     HTML element
	 * @param 
	 *     Element's attribute(s)
	 */
	function openHandler(&$parser, $element, $attr)
	{
		$result  = '';
		$element = strtolower($element);
		$result .= $this->endOfEmpty();
		if (in_array($element, $this->voidElement)) {
			$this->voidLevel++;
		} elseif ($this->voidLevel == 0 && in_array($element, $this->nonRemoveTags)) {
			switch ($element) {
				case 'img' :
					$result .= $this->openHandler_img($attr);
					break;
				case 'a' :
					$result .= $this->openHandler_a($attr);
					break;
				default :
					$result .= $this->openHandler_default($element, $attr);
					break;
			}
		}
		$this->result .= $result;
	}

	// }}}
	// {{{ openHandler_img($attr)

	/**
	 * Process img tag.
	 *
	 * @param 
	 *     Image element's attribute(s)
	 *
	 * @return string
	 *     HTML TAG
	 */
	function openHandler_img($attr)
	{
		$result  = '';
		$altText = '';
		if (isset($attr['alt'])) {
			$altText = $attr['alt'];
		}
		switch ($this->imageMode) {
			case 'text':
				if ($altText !== '') {
					$result       .= $altText;
				}
				break;
			case 'link':
				if ($altText === '') {
					$altText = '[image]';
				}
				$newAttr         = $attr;
				$newAttr['href'] = $newAttr['src'];
				unset($newAttr['src']);
				unset($newAttr['alt']);
				$result .= $this->openHandler_a($newAttr);
				$result .= $altText;
				break;
			default:
				$result .= $this->openHandler_default('img', $attr);
		}
		return $result;
	}

	// }}}
	// {{{ openHandler_a($attr)

	/**
	 * Process img tag.
	 *
	 * @param 
	 *     A element's attribute(s)
	 *
	 * @return string
	 *     HTML TAG
	 */
	function openHandler_a($attr)
	{
		$result = '';
		if ($this->aNestLevel == 0) {
			$result .= $this->openHandler_default('a', $attr);
		}
		$this->aNestLevel++;
		return $result;
	}

	// }}}
	// {{{ openHandler_default($element, $attr)

	/**
	 * Process img tag.
	 *
	 * @param 
	 *     HTML element
	 * @param 
	 *     Element's attribute(s)
	 *
	 * @return string
	 *     HTML TAG
	 */
	function openHandler_default($element, $attr)
	{
		$emptyAttrs = array(
			'checked',
			'disabled',
			'readonry',
			'ismap',
			'multiple',
			'selected',
		);
		$result  = '';
		$tagName = strtolower($element);
		$close   = '';
		if (in_array($element, $this->emptyElement)) {
			$this->isEmpty = true;
		} else {
			$this->isEmpty = false;
		}
		$result .= '<' . $element;
		foreach ($attr as $name => $value) {
			$name = strtolower($name);
			if (in_array($name, $this->nonRemoveAttrs)) {
				switch ($this->mimeType) {
					case 'application/xhtml+xml':
						if (in_array($name, $emptyAttrs) && empty($value)) {
							$value = $name;
						}
						$result .= ' ' . $name . '="' . $value . '"';
						break;
					case 'text/html':
						if (in_array($name, $emptyAttrs)) {
							$result .= " $name";
						} elseif ($this->quoteMode
								&& !empty($value)
								&& strpos($value, ' ') === FALSE) {
							$result .= " $name=$value";
							if (!$this->isEmpty) {
								$close = ' ';
							}
						} else {
							$result .= ' ' . $name . '="' . $value . '"';
						}
						break;
				}
			}
		}
		if (!$this->isEmpty) {
			$close .= '>';
		}
		$result .= $close;
		return $result;
	}

	// }}}
	// {{{ closeHandler(&$parser, $element)

	/**
	 * Process close tag.
	 *
	 * @param
	 *
	 * @param 
	 *     HTML element
	 */
	function closeHandler(&$parser, $element)
	{
		$result  = '';
		$tagName = strtolower($element);
		if (in_array($tagName, $this->voidElement)) {
			$this->voidLevel--;
		} else if ($this->voidLevel == 0 && in_array($element, $this->nonRemoveTags)) {
			switch ($element) {
				case 'img' :
					$result .= $this->closeHandler_img();
					break;
				case 'a' :
					$result .= $this->closeHandler_a();
					break;
				default :
					$result .= $this->closeHandler_default($element);
					break;
			}
		}
		$this->result .= $result;
	}

	// }}}
	// {{{ closeHandler_img()

	/**
	 * Process img tag.
	 *
	 * @return string
	 *     HTML TAG
	 */
	function closeHandler_img()
	{
		$result = '';
		switch ($this->imageMode) {
			case 'text':
				break;
			case 'link':
				$result .= $this->closeHandler_a();
				break;
			default:
				$result .= $this->closeHandler_default('img');
		}
		return $result;
	}

	// }}}
	// {{{ closeHandler_a()

	/**
	 * Process a tag.
	 *
	 * @return string
	 *     HTML TAG
	 */
	function closeHandler_a()
	{
		$result = '';
		$this->aNestLevel--;
		if ($this->aNestLevel == 0) {
			$result .= $this->closeHandler_default('a');
		}
		return $result;
	}

	// }}}
	// {{{ closeHandler_default($element)

	/**
	 * Process close tag.
	 *
	 * @param 
	 *     HTML element
	 *
	 * @return string
	 *     HTML TAG
	 */
	function closeHandler_default($element)
	{
		$result  = '';
		$tagName = strtolower($element);
		if ($this->isEmpty) {
			switch ($this->mimeType) {
				case 'application/xhtml+xml':
					$result .= (' />');
					break;
				case 'text/html':
					$result .= ('>');
					break;
			}
		} else {
			$result .= ('</' . $element . '>');
		}
		$this->isEmpty = false;
		return $result;
	}

	// }}}
	// {{{ dataHandler(&$parser, $data)

	/**
	 * When it's in the script tag, everything is eliminated,
	 * and when it isn't so, it's returned just as it is.
	 *
	 * @param 
	 *
	 *
	 * @param string
	 *     HTML contents text
	 */
	function dataHandler(&$parser, $data)
	{
		$result  = '';
		$result .= $this->endOfEmpty();
		if ($this->voidLevel == 0) {
			$result .= $data;
		}
		$this->result .= $result;
	}

	// }}}
	// {{{ endOfEmpty()

	/**
	 * Empty tag is closed.
	 *
	 * @return string
	 *     TAG tail
	 */
	function endOfEmpty()
	{
		$result = '';
		if ($this->isEmpty) {
			switch ($this->mimeType) {
				case 'application/xhtml+xml':
					$result .= (' />');
					break;
				case 'text/html':
					$result .= ('>');
					break;
			}
		}
		$this->isEmpty = false;
		return $result;
	}

	// }}}
	// {{{ getResult()

	/**
	 * A result is faired, and it outputs.
	 *
	 * @return string
	 *     HTML contents
	 */
	function getResult()
	{
		$strHTML = $this->result;
		$strHTML = preg_replace("!(\r|\n|\n\r)!", "\n", $strHTML);
		$strHTML = preg_replace("!\n\s+!", "\n", $strHTML);
		$strHTML = preg_replace("!\s+\n!", "\n", $strHTML);
		$strHTML = preg_replace("!\n+!", "\n", $strHTML);
		$strHTML = preg_replace("!^\n!", "", $strHTML);
		return $strHTML;
	}




















}



